#include "Vector3D.h"
#include <math.h>
#include "GullConfiguration.h"

using namespace gull::core;
using namespace std;


void
Vector3D::testClass()
{
    Vector3D v1;
    Vector3D v2(1,1,0);
    Vector3D v3(1,0,0);
    Vector3D v4(0,1,0);

    cout << v2[0] << endl;
    cout << v2[1] << endl;
    cout << v2[2] << endl;
    
    cout << -v2 << endl;
    cout << v2 << endl;
    
    
    cout << (v2 != v1 ? "true" : "false") << endl;
    cout << (v2 == v1 ? "true" : "false")  << endl;
    cout << (v2 == v2 ? "true" : "false")  << endl;
    
    cout << (v2 / 0) << endl;
    cout << v4.cross(v3) << endl;
    cout << v2.length() << endl;
    cout << (v3.cross(v4)).dot(v4) << endl;
    cout << (v2 *= 4) << endl;
    cout << v2.normalize() << endl;
    cout << (v2/= 0) << endl;
    v2[2] = 3;
    cout << v2 << endl;
    
    
}


Vector3D
Vector3D::operator-() const
{
    return Vector3D(-m_x, -m_y, -m_z);
    
}

bool 
Vector3D::operator== (const Vector3D& v) const
{
    return (v.x() == m_x && v.y() == m_y && v.z() == m_z);
}

bool 
Vector3D::operator!= (const Vector3D& v) const
{
    return (!(v == (*this)));
}


ostream& 
operator<<(ostream& o, const Vector3D& v)
{
    o << "[" << v.x() << "," << v.y() << "," << v.z() <<"]";
    return o;
}

Vector3D::Vector3D(double x , double y, double z):
m_x(x),
m_y(y),
m_z(z)
{
#if GULL_CREATION_DESTRUCTION_DEBUG
    cout << "Vector3D constructed: " << this << endl;
#endif

}

Vector3D::Vector3D(const Vector3D& v)
{
    
    if(&v != this)
    {
        m_x = v[0];
        m_y = v[1];
        m_z = v[2];
        
    }
#if GULL_CREATION_DESTRUCTION_DEBUG
    cout << "Vector3D constructed: " << this << endl;
#endif
}

Vector3D::~Vector3D()
{
#if GULL_CREATION_DESTRUCTION_DEBUG
    cout << "Vector3D deleted: " << this << endl;
#endif
}

Vector3D 
Vector3D::operator+(const Vector3D& v) const
{
    return Vector3D(v.x() + m_x, v.y() + m_y, v.z() + m_z);
}

Vector3D
Vector3D::operator-(const Vector3D& v) const
{
    return Vector3D(m_x - v.x(), m_y - v.y(), m_z - v.z());
}


Vector3D
Vector3D::operator/(double a) const
{
    if(a == 0)
        return Vector3D(*this);
    return Vector3D(m_x/a,m_y/a, m_z/a);
}

Vector3D
Vector3D::operator*(double a) const
{
    return Vector3D(m_x*a,m_y*a, m_z*a);
}

Vector3D& 
Vector3D::operator=(const Vector3D& v)
{
    m_x = v.x();
    m_y = v.y();
    m_z = v.z();
    return (*this);
}

Vector3D&
Vector3D::operator*=(double a)
{
    m_x *= a;
    m_y *= a;
    m_z *= a;
    return (*this);
}


Vector3D&
Vector3D::operator/=(double a)
{
    if(a != 0)
    {
        m_x /= a;
        m_y /= a;
        m_z /= a;
    }       
    return (*this);
    
}

Vector3D& 
Vector3D::operator+=(const Vector3D& v)
{
    m_x += v.x();
    m_y += v.y();
    m_z += v.z();
    return (*this);
    
}

Vector3D&
Vector3D::operator-=(const Vector3D& v)
{
    m_x -= v.x();
    m_y -= v.y();
    m_z -= v.z();
    return (*this);
}

double
Vector3D::length() const
{
    return sqrt((m_x*m_x + m_y*m_y + m_z*m_z));
}

const Vector3D& 
Vector3D::normalize()
{
    (*this)/=length();
    return (*this);
}

double 
Vector3D::dot(const Vector3D& v) const
{
    return (m_x*m_x + m_y*m_y + m_z*m_z);
}

Vector3D 
Vector3D::cross(const Vector3D& v) const
{
    return Vector3D(m_y*v.z() - m_z*v.y(),
                    m_z*v.x() - m_x*v.z(),
                    m_x*v.y() - m_y*v.x());
}
